/** 图形点类
 *
 */
export class Point {
  readonly x: number;
  readonly y: number;

  /** 构造函数，可以传入x和y参数，如果不传入，则x和y的值为NaN
   *
   * @param x x坐标
   * @param y y坐标
   */
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }

  /** 判断Point是否是NaN
   *
   * @param p 点
   * @returns boolean
   */
  // static isNaN(p: Point): boolean {
  //   return isNaN(p.x) || isNaN(p.y);
  // }

  /** 计算两个Point之间的距离
   *
   * @param p1 点1
   * @param p2 点1
   * @returns 两点距离
   */
  static distance(p1: Point, p2: Point): number {
    return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
  }
}

/** 精度控制范围
 *
 */
const accuracyRange: number = 1e-6;

/** 线段类 */
export class Line {
  constructor(public start: Point, public end: Point) {}

  /** 线段长度 */
  get length() {
    return Point.distance(this.start, this.end);
  }

  /** 点是否在此线段上
   *
   * @param p 点p
   * @returns boolean
   */
  inLine(p: Point) {
    const left = Point.distance(this.start, p);
    const right = Point.distance(this.end, p);
    if (Math.abs(left + right - this.length) <= accuracyRange) return true;
    return false;
  }

  /** 线段斜率
   *
   * @returns
   */
  slope() {
    return Line.sloped(this);
  }

  /** 计算线的角度
   *
   * @returns
   */
  angle() {
    return (
      (Math.atan2(this.end.y - this.start.y, this.end.x - this.start.x) * 180) /
      Math.PI
    );
  }

  /** 计算线的斜率
   *
   * @param line 线
   * @returns
   */
  static sloped(line: Line) {
    return (line.end.y - line.start.y) / (line.end.x - line.start.x);
  }

  /** 判断两线段是否平行
   *
   * @param lineM 线段m
   * @param lineN 线段n
   * @returns boolean
   */
  static IsParallel(lineM: Line, lineN: Line): boolean {
    const flagA: boolean =
      Math.abs(lineM.end.x - lineM.start.x) <= accuracyRange; //m垂线
    const flagB: boolean =
      Math.abs(lineN.end.x - lineN.start.x) <= accuracyRange; //n垂线
    // 两垂直线
    if (flagA && flagB) {
      return true;
    } else if (flagA || flagB) {
      //一条垂直一条不垂直
      return false;
    } else {
      return Math.abs(lineM.slope()! - lineN.slope()!) <= accuracyRange;
    }
  }

  /** 两线段交点
   *
   * @param lineM 线段m
   * @param lineN 线段n
   * @returns
   */
  static IntersectWith(lineM: Line, lineN: Line) {
    if (this.IsParallel(lineM, lineN)) {
      return null;
    }
    let inter_x: number, inter_y: number;
    const m_s = lineM.start,
      m_e = lineM.end,
      n_s = lineN.start,
      n_e = lineN.end;
    //直线m 的斜率 k1=lineM.slope();
    //直线方程 y=k1(x-m_s.x)+m_s.y=>y=k1*x+(m_s.y-k1*m_s.x)
    if (
      Math.abs(m_e.x - m_s.x) > accuracyRange &&
      Math.abs(n_e.x - n_s.x) > accuracyRange
    ) {
      const k1 = lineM.slope(),
        k2 = lineN.slope();
      const b1 = m_s.y - k1 * m_s.x,
        b2 = n_s.y - k2 * n_s.x;
      //平行线已经屏蔽；
      //k1*x+b1=k2*x+b2=>x=(b2-b1)/(k1-k2);
      inter_x = (b2 - b1) / (k1 - k2);
      inter_y = k1 * inter_x + b1;
      return new Point(inter_x, inter_y);
    } else if (
      Math.abs(m_e.x - m_s.x) <= accuracyRange &&
      Math.abs(n_e.x - n_s.x) > accuracyRange
    ) {
      const k2 = lineN.slope();
      const b2 = n_s.y - k2 * n_s.x;
      inter_x = n_s.x;
      inter_y = k2 * inter_x + b2;
      return new Point(inter_x, inter_y);
    } else if (
      Math.abs(m_e.x - m_s.x) > accuracyRange &&
      Math.abs(n_e.x - n_s.x) <= accuracyRange
    ) {
      const k1 = lineM.slope();
      const b1 = m_s.y - k1 * m_s.x;
      inter_x = m_s.x;
      inter_y = k1 * inter_x + b1;
      return new Point(inter_x, inter_y);
    } else {
      return null;
    }

    // // 计算线段M的斜率
    // const a_m = lineM.end.y - lineM.start.y;
    // const b_m = lineM.end.x - lineM.start.x;
    // const c_m = lineM.end.x * lineM.start.y - lineM.start.x * lineM.end.y;

    // // 计算线段N的斜率
    // const a_n = lineN.end.y - lineN.start.y;
    // const b_n = lineN.end.x - lineN.start.x;
    // const c_n = lineN.end.x * lineN.start.y - lineM.start.x * lineM.end.y;

    // let istptx: number, istpty: number;
    // // 计算线段M和线段N的交点
    // if (Math.abs(a_m) <= accuracyRange) {
    //   // 计算线段M的交点y坐标
    //   istpty = (-1 * c_m) / b_m;
    //   // 计算线段N的交点x坐标
    //   if (Math.abs(b_n) <= accuracyRange) {
    //     istptx = (-1 * c_n) / a_n;
    //   } else {
    //     istptx = (-1 * (b_n * istpty + c_n)) / a_n;
    //   }
    // } else if (Math.abs(b_m) <= accuracyRange) {
    //   // 计算线段M的交点x坐标
    //   istptx = (-1 * c_m) / a_m;
    //   // 计算线段N的交点y坐标
    //   if (Math.abs(a_n) <= accuracyRange) {
    //     istpty = (-1 * c_n) / b_n;
    //   } else {
    //     istpty = (-1 * (a_m * istptx + c_m)) / b_m;
    //   }
    // } else if (Math.abs(b_n) <= accuracyRange) {
    //   // 计算线段N的交点x坐标
    //   istptx = (-1 * c_n) / a_n;
    //   // 计算线段M的交点y坐标
    //   if (Math.abs(a_m) <= accuracyRange) {
    //     istpty = (-1 * c_m) / b_m;
    //   } else {
    //     istpty = (-1 * (a_n * istptx + c_n)) / b_n;
    //   }
    // } else {
    //   // 计算线段N的交点x坐标
    //   istptx = (b_n * c_m - b_m * c_n) / (a_n * b_m - a_m * b_n);
    //   istpty = (-1 * (a_m * istptx + c_m)) / b_m;
    // }

    // // 计算线段M的起始点的x坐标
    // const minX_m = Math.min(lineM.start.x, lineM.end.x);
    // // 计算线段M的结束点的x坐标
    // const maxX_m = Math.max(lineM.start.x, lineM.end.x);
    // // 计算线段M的起始点的y坐标
    // const minY_m = Math.min(lineM.start.y, lineM.end.y);
    // // 计算线段M的结束点的y坐标
    // const maxY_m = Math.max(lineM.start.y, lineM.end.y);
    // // 判断线段M的交点是否在线段M的范围内
    // const flagA =
    //   istptx >= minX_m - accuracyRange &&
    //   istptx <= maxX_m + accuracyRange &&
    //   istpty >= minY_m - accuracyRange &&
    //   istpty <= maxY_m + accuracyRange;

    // // 计算线段N的起始点的x坐标
    // const minX_n = Math.min(lineN.start.x, lineN.end.x);
    // // 计算线段N的结束点的x坐标
    // const maxX_n = Math.max(lineN.start.x, lineN.end.x);
    // // 计算线段N的起始点的y坐标
    // const minY_n = Math.min(lineN.start.y, lineN.end.y);
    // // 计算线段N的结束点的y坐标
    // const maxY_n = Math.max(lineN.start.y, lineN.end.y);
    // // 判断线段N的交点是否在线段N的范围内
    // const flagB =
    //   istptx >= minX_n - accuracyRange &&
    //   istptx <= maxX_n + accuracyRange &&
    //   istpty >= minY_n - accuracyRange &&
    //   istpty <= maxY_n + accuracyRange;

    // // 如果线段M和线段N的交点都满足条件，则返回线段M和线段N的交点
    // if (flagA && flagB) {
    //   return new Point(istptx, istpty);
    // }
    // return null;
  }

  /** 点到线段的距离；
   * 利用三角形海伦面积公式；
   * @param point 点
   * @param line 线
   * @returns 距离
   */
  static pointToLine(point: Point, line: Line): number {
    const len = line.length;
    const c_a = Point.distance(point, line.start);
    const c_b = Point.distance(point, line.end);

    if (c_a + c_b <= len) {
      return 0;
    } else {
      const s = this.trianglesArea(c_a, c_b, len);
      return (2 * s!) / len;
    }
  }

  /** 三角形面积公式；
   * 秦九昭面积公式：s=math.sqrt(p*(p-a)*(p-b)*(p-c)),其中p为半周长；
   * @param a 边长a
   * @param b 边长b
   * @param c 边长c
   * @returns 面积
   */
  static trianglesArea(a: number, b: number, c: number) {
    if (a + b <= c || a + c <= b || b + c <= a) {
      //三角形边长判断
      return null;
    } else {
      const p = (a + b + c) / 2;
      return Math.sqrt(p * (p - a) * (p - b) * (p - c));
    }
  }
}
